﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;

namespace HodhodNews.Extraction
{
    public static class HtmlNodeExtensions2
    {
        static string[] nodesToBeRemoved = new string[]{
            "script",
            "iframe",
            "input"
        };
        public static bool ShouldBeRemoved(this HtmlNode node)
        {
            if (nodesToBeRemoved.Contains(node.Name))
                return true;
            if (node.Name == "p" && node.InnerText.Trim().Length == 0)
                return true;

            return node.IsCommentContainer();
        }
    }
    public class NoiseCancellator
    {

        string[] nodesToBeRemovedIfEmpty = new string[]{
            "p"
        };
        public NoiseCancellator()
        {
            this.NoisyNodes = new List<HtmlNode>();
        }
        public List<HtmlNode> NoisyNodes { get; set; }
        public ExtracationOptions Options { get; set; }

        HtmlNode GetDivNode(HtmlNode node)
        {

            while (node != null)
            {
                if (node.Name == "div")
                {
                    return node;
                }
                node = node.ParentNode;
            }
            return node;
        }
        public void CancelNoise(HtmlNode node, ExtracationOptions options)
        {
            this.Options = options;
            RemoveNoisyNodes(node);

            foreach (var n in node.ChildNodes)
            {
                HowManyCssClassUnder(n);
            }
            var counter = GetHeighestCssClassCounter(node);
            if (counter != null && EnsureItIsCommentsNode(counter))
            {
                RemoveNodeHasNoisyComments(node, counter.Node);
            }
        }

        void RemoveNoisyNodes(HtmlNode node)
        {

            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                var currentNode = node.ChildNodes[i];
                var isEmpty = currentNode.InnerText.Trim().Length == 0;

                if (node.ShouldBeRemoved())
                {
                    currentNode.Remove();
                    i--;
                    continue;
                }

                if (NeedsUrlAdjustment(currentNode))
                {
                    int length = this.Options.NewsUrl.LastIndexOf("/") + 1;
                    string urlBase = this.Options.NewsUrl.Substring(0, length);
                    var attribute = currentNode.Attributes["src"] == null ? currentNode.Attributes["href"] : currentNode.Attributes["src"];
                    if (attribute == null)
                        goto endif;

                    string imageSrc = attribute.Value.ToLower();
                    string prefix = "";
                    string suffix = imageSrc;

                    if (imageSrc.StartsWith(".") || imageSrc.StartsWith("/"))
                    {
                        int firstBackSlach = imageSrc.IndexOf("/");

                        if (firstBackSlach != -1)
                        {
                            prefix = imageSrc.Substring(0, firstBackSlach);
                            suffix = imageSrc.Substring(firstBackSlach, imageSrc.Length - firstBackSlach); ;
                        }
                        if (imageSrc.IndexOf("/") == -1)
                        {
                            attribute.Value = urlBase + "/" + imageSrc;
                            goto endif;
                        }
                        else if (string.IsNullOrWhiteSpace(prefix) && suffix.StartsWith("/"))
                        {
                            int start = urlBase.IndexOf("://") + "://".Length;
                            int lastBackSlach = urlBase.IndexOf("/", start);

                            urlBase = urlBase.Substring(0, lastBackSlach);


                            //int preLastBackSlach = urlBase.IndexOf("/", start);
                            
                            ////http://www.shorouknews.com/kkdkd/contentdata.aspx?id=486210
                            //int counter = 0;
                            //while (lastBackSlach != -1)
                            //{
                            //    if (counter == 2)
                            //    {
                            //        preLastBackSlach = lastBackSlach;
                            //        counter = 0;
                            //    }
                            //    lastBackSlach = urlBase.IndexOf("/", lastBackSlach + 1);
                            //    counter++;
                            //}

                            //length = preLastBackSlach;
                            //urlBase = urlBase.Substring(0, length);
                        }
                    }
                    if (!urlBase.EndsWith("/") && !suffix.StartsWith("/"))
                    {
                        urlBase += "/";
                    }

                    string imageUrl = urlBase + suffix;
                    attribute.Value = imageUrl;


                }
            endif:
                RemoveNoisyNodes(currentNode);
            }
        }

        private static bool NeedsUrlAdjustment(HtmlNode currentNode)
        {
            var srcAttribute = currentNode.Attributes["src"];
            var hrefAttribute = currentNode.Attributes["href"];

            if (currentNode.Name == "img" && srcAttribute != null && !srcAttribute.Value.ToLower().StartsWith("http") ||
                (currentNode.Name == "a" && hrefAttribute != null && !hrefAttribute.Value.ToLower().StartsWith("http")))
            {
                return true;
            }
            return false;
        }

        bool EnsureItIsCommentsNode(CssClassCounter counter)
        {
            bool allChildsHasOneCssClass = true;
            foreach (var n in counter.Node.ChildNodes)
            {
                if (n.Name == "#text")
                    continue;

                if (n.Attributes != null && n.Attributes[counter.CssClass] != null && n.Attributes[counter.CssClass].Value == "1")
                {
                    allChildsHasOneCssClass &= true;
                }
                else
                {
                    allChildsHasOneCssClass &= false;
                }
            }
            return allChildsHasOneCssClass;

        }
        private void RemoveNodeHasNoisyComments(HtmlNode node, HtmlNode nodeWithNoisyComments)
        {
            if (nodeWithNoisyComments != null)
            {
                while (true)
                {
                    foreach (var n in node.ChildNodes)
                    {
                        if (n == nodeWithNoisyComments)
                        {
                            node.ChildNodes.Remove(nodeWithNoisyComments);
                            return;
                        }
                    }
                    nodeWithNoisyComments = nodeWithNoisyComments.ParentNode;
                }
            }
        }
        void HowManyCssClassUnder(HtmlNode node)
        {
            var attribute = node.Attributes["class"];
            if (attribute != null)
            {
                WriteCssClassCountToParents(node.ParentNode, attribute.Value);
            }
            foreach (var n in node.ChildNodes)
            {
                HowManyCssClassUnder(n);
            }
        }
        void WriteCssClassCountToParents(HtmlNode node, string cssClassName)
        {
            if (node == null)
                return;

            string counterName = cssClassName + "__Count";
            var counterAttribute = node.Attributes[counterName];

            if (counterAttribute != null)
            {
                int count = Convert.ToInt32(counterAttribute.Value);
                counterAttribute.Value = (++count) + "";
                if (count >= 2)
                    return;
            }
            node.SetAttributeValue(counterName, "1");
            WriteCssClassCountToParents(node.ParentNode, cssClassName);
        }
        List<HtmlAttribute> GetCounterAttributes(HtmlNode node)
        {

            var c = new List<HtmlAttribute>();
            if (node.Attributes == null)
                return c;

            foreach (var a in node.Attributes)
            {
                if (a.Name.EndsWith("__count"))
                    c.Add(a);
            }
            return c;
        }
        CssClassCounter GetHeighestCssClassCounter(HtmlNode node)
        {
            var list = new List<CssClassCounter>();
            foreach (var n in node.ChildNodes)
            {
                AddCssClassCounters(n, list);
            }
            if (list.Count > 0)
            {
                list.Sort((n, n2) => n.Count.CompareTo(n2.Count));
                return list[list.Count - 1];
            }
            return null;
        }

        void AddCssClassCounters(HtmlNode node, List<CssClassCounter> list)
        {
            var attributes = GetCounterAttributes(node);
            if (attributes.Count > 0)
            {
                foreach (var attr in attributes)
                {
                    list.Add(new CssClassCounter()
                    {
                        Node = node,
                        Count = Convert.ToInt32(attr.Value),
                        CssClass = attr.Name
                    });
                }
            }

            foreach (var n in node.ChildNodes)
            {
                AddCssClassCounters(n, list);
            }

        }

    }
}
